//
// Created by Kai Wang on 23-11-21.
//

#ifndef OPENCOS_ORDER_H
#define OPENCOS_ORDER_H

#include "customer/customer.h"
#include "item/item.h"
#include "order/order_comment.h"
#include "order/order_status.h"

#include <algorithm>
#include <chrono>
#include <fmt/format.h>
#include <rapidcsv.h>
#include <vector>

class OrderItem : public std::pair<Item, int>
{
public:
    [[nodiscard]] float       get_price() const;
    [[nodiscard]] std::string get_info() const;
};

class Order
{
public:
    Order() = default;
    Order(
        int                    serial_number,
        time_point             create_time,
        time_point             finish_time,
        time_point             cancel_time,
        int                    customer_id,
        std::vector<OrderItem> ordered_items,
        float                  total_price,
        // OrderComment           order_comment,
        OrderStatus order_status
    );
    int get_serial_number() const;
    // time points
    const time_point &get_create_time() const;
    const time_point &get_finish_time() const;
    const time_point &get_cancel_time() const;

    void set_create_time(const time_point &create_time);
    void set_finish_time(const time_point &finish_time);
    void set_cancel_time(const time_point &cancel_time);

    [[nodiscard]] int get_customer_id() const;

    float                     get_total_price() const;
    [[nodiscard]] float       get_original_total_price() const;
    void                      set_status(OrderStatus status);
    [[nodiscard]] OrderStatus get_status() const;

    [[nodiscard]] std::vector<OrderItem> &get_ordered_items();

    [[nodiscard]] Customer      get_customer() const;
    [[nodiscard]] OrderComment &get_order_comment();

    void calc_total_price();

    void set_customer(const Customer &customer);

    int get_serial_number()
    {
        return serial_number_;
    }

    void set_serial_number(int serial_number)
    {
        serial_number_ = serial_number;
    }

private:
    // time points
    time_point create_time_;
    time_point finish_time_;
    time_point cancel_time_;

    // order id
    int serial_number_ = -1;

    // customer
    int      customer_id_;
    Customer customer_;

    // ordered dishes
    std::vector<OrderItem> ordered_items_;
    // total_price
    float original_total_price_;
    float total_price_;
    // ordered items' comments
    OrderComment order_comment_;

    OrderStatus status_ = OrderStatus::INVALID;
};

namespace Convert
{
/**
 *
 * @param tp std::chrono::system_clock::time_point to be convert
 * @param str format Www Mmm dd hh:mm:ss yyyy\n
 */
void TimePointToString(const time_point &tp, std::string &str);

/**
 * @brief convert string to time_point
 * @param str
 * @param tp
 */
void StringToTimePoint(const std::string &str, time_point &tp);

/**
 * @LastEditors: Even
 * @Date: 2023-12-28 20:49:10
 * @Description:Overload time_point to string
 */
std::string TimePointToString(const time_point &tp);
/**
 * @brief convert OrderItem to string, with format item*x
 * @param item
 * @param str
 */

void OrderItemToString(const OrderItem &order_item, std::string &str);
/**
 * @LastEditors: Even
 * @Date: 2023-12-28 22:51:56
 * @Description: overload convert OrderItem to string, with format item*x
 * @param {OrderItem} &order_item
 */

std::string OrderItemToString(const OrderItem &order_item);
/**
 * @LastEditors: Even
 * @Date: 2023-12-28 23:20:29
 * @Description:
 * @param {vector<OrderItem>} &order_items
 */
std::string OrderItemsToString(const std::vector<OrderItem> &order_items);
/**
 * @brief convert string to OrderItem, with format item*x to OrderItem(item, x);
 * @param str
 * @param item
 */
void StringToOrderItem(const std::string &str, OrderItem &order_item);

/**
 * @brief convert string to std::vector<OrderItem>
 * @param status
 * @param str
 */
void StringToOrderItems(const std::string &str, std::vector<OrderItem> &order_items);
}  // namespace Convert

#endif  // OPENCOS_ORDER_H
